package dell.yangzhou;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//动态代理的举例
interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }
    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}
class HumanUtil{
    public void method1(){
        System.out.println("======通用方法一！======");
    }
    public void method2(){
        System.out.println("======通用方法二！======");
    }
}
class ProxyFactory{
    //调用此方法返回一个代理类对象
    public  static Object getProxyInstance(Object obj){
        //obj为被代理类的对象
        MyInvocationHandler handler=new MyInvocationHandler();
       handler.bind(obj);
       //调用此方法时，就可实现自动给obj实例化（因为在下面已经实例化过了）
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), handler);
       //返回代理类对象
        //obj.getClass().getClassLoader()是哪个类的加载器加载的，与其一样(第一个参数)
        //obj.getClass().getInterfaces()被代理类实现的是哪个接口，代理类就与其一样，都实现这个接口（第二个参数）
    }
}
class MyInvocationHandler implements InvocationHandler{
//当我们通过代理类的对象，调用方法a时，就会自动的调用invoke（）方法
    //因为会被自动调用的原因，把需要被自动调用的被代理类方法写入invoke（）方法就完美了
    private Object obj;//声明被代理类对象（需要具有动态性，不能直接写死为Human类型，用Object类型来定义）
    //但要注意，在需要使用的时候，要用被代理类对象进行赋值
   public void bind(Object obj){
       this.obj=obj;
       //构造器实例化obj
   }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil util=new HumanUtil();
        util.method1();

       //参数： proxy：代理类的对象，method：代理类的对象去调用的哪个方法
        //代理类对象方法时，要用到被代理类对象的这个同名方法，所以要去定义一个被代理类对象
        Object returnValue = method.invoke(obj, args);
        //method():即为代理类对象调用的方法，也就作为了被代理类对象要调用的方法
        //obj：被代理类对象

        util.method2();

        return returnValue;
        //将代理类对象调用的方法得到的返回值就作为invoke（）方法的返回值
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        //创建代理类对象,参数传入被代理类对象
        SuperMan superMan=new SuperMan();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
       //当通过代理类对象调用方法时就会自动调用被代理类中同名的方法
        proxyInstance.getBelief();
        proxyInstance.eat("酸辣粉！");

        System.out.println("================");
        //根据接口和被代理类动态调用
        NikeClothFactory nikeClothFactory=new NikeClothFactory();
        //创建被代理类对象
       ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        //动态创建代理类对象
        proxyClothFactory.produceCloth();
    }
}
